package com.filyar.htyd.exception;

import cn.dev33.satoken.exception.NotLoginException;
import com.alibaba.fastjson.JSONObject;
import com.filyar.htyd.common.Constants;
import com.filyar.htyd.common.ExceptionEnum;
import com.filyar.htyd.common.Result;
import com.filyar.htyd.component.webhook.FeishuWebhook;
import com.filyar.htyd.component.webhook.WechatWebhook;
import com.filyar.htyd.entity.config.WebhookConfig;
import com.filyar.htyd.utils.IpUtils;
import com.filyar.htyd.utils.RedisUtils;
import com.filyar.htyd.utils.RegionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @Resource
    private WechatWebhook wechatWebhook;
    @Resource
    private FeishuWebhook feishuWebhook;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedisUtils redisUtils;

    /**
     * 如果抛出的的是ServiceException，则调用该方法
     * @param se 业务异常
     * @return Result
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result handle(Exception se) {
        String strStackTrace = getStackTraceAsString(se);
        HttpServletRequest request = RequestUtils.getCurrentRequest();
        ExceptionMessage exceptionMessage = buildExceptionMessage(request);
        if (se instanceof ServiceException) {
            ServiceException serviceException = (ServiceException) se;
            exceptionMessage.setType(Constants.CUSTOM_EXCEPTION);
            exceptionMessage.setCode(serviceException.getCode());
            exceptionMessage.setMessage(serviceException.getMessage());
            if (serviceException.getCode().equals(ExceptionEnum.PLEASE_LOGIN.getCode())){
                log.error("用户未登录，ip: {}, method: {}, queryString: {}, requestURI: {}, cityInfo: {}", exceptionMessage.getIp(), exceptionMessage.getMethod(), exceptionMessage.getQueryString(), exceptionMessage.getRequestURI(), exceptionMessage.getCityInfo());
            }else {
                log.error("业务异常，ip: {}, method: {}, queryString: {}, requestURI: {}, cityInfo: {}, code: {}, message: {}", exceptionMessage.getIp(),exceptionMessage.getMethod(), exceptionMessage.getQueryString(), exceptionMessage.getRequestURI(), exceptionMessage.getCityInfo(), serviceException.getCode(), serviceException.getMessage());
            }
            if (serviceException.getNeedToSend()) {
                send(exceptionMessage);
            }
            return Result.error(serviceException.getCode(), se.getMessage());
        }else if (se instanceof NotLoginException) {
            return Result.error(ExceptionEnum.PLEASE_LOGIN.getCode(), ExceptionEnum.PLEASE_LOGIN.getMsg());
        } else {
            log.error(se.getMessage(),se);
            String exceptionType = strStackTrace.substring(0, strStackTrace.indexOf("\t"));
            exceptionMessage.setType(exceptionType);
            exceptionMessage.setCode(Constants.SYSTEM_EXCEPTION_CODE);
            exceptionMessage.setMessage(strStackTrace);
            log.error("系统异常，ip: {}, method: {}, queryString: {}, requestURI: {}, cityInfo: {}, exceptionType: {}", exceptionMessage.getIp(), exceptionMessage.getMethod(), exceptionMessage.getQueryString(), exceptionMessage.getRequestURI(), exceptionMessage.getCityInfo(), exceptionType);
            send(exceptionMessage);
            return Result.error("1", "服务器错误");
        }
    }

    private void send(ExceptionMessage exceptionMessage){
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(Constants.WEBHOOK_CONFIG))) {
            String webhookConfigStr = redisUtils.getValueFromRedis(Constants.WEBHOOK_CONFIG);
            List<WebhookConfig> webhookConfigs = JSONObject.parseArray(webhookConfigStr, WebhookConfig.class);
            for (WebhookConfig webhookConfig : webhookConfigs) {
                if (webhookConfig.getNeedNotify()) {
                    if (webhookConfig.getPlatform().equals(Constants.WECHAT)) {
                        exceptionMessage.setUrl(webhookConfig.getUrl());
                        try {
                            wechatWebhook.sendSystemException(exceptionMessage);
                        } catch (Exception e) {
                            log.error("微信webhook失败，错误信息: {}", e.getMessage());
                        }
                    } else if (webhookConfig.getPlatform().equals(Constants.FEISHU)) {
                        exceptionMessage.setUrl(webhookConfig.getUrl());
                        try {
                            feishuWebhook.sendSystemException(exceptionMessage);
                        } catch (Exception e) {
                            log.error("飞书webhook失败，错误信息: {}", e.getMessage());
                        }
                    } else {
                        log.error("未知的平台类型");
                    }
                }
            }
        }
    }

    private ExceptionMessage buildExceptionMessage(HttpServletRequest request) {
        ExceptionMessage exceptionMessage = new ExceptionMessage();
        if (request != null) {
            String method = request.getMethod();
            String ip = IpUtils.getIpAddress(request);
            String queryString = request.getQueryString();
            String requestURI = request.getRequestURI();
            String cityInfo = RegionUtil.getCityInfo(ip);

            exceptionMessage.setIp(ip);
            exceptionMessage.setMethod(method);
            exceptionMessage.setQueryString(queryString);
            exceptionMessage.setRequestURI(requestURI);
            exceptionMessage.setCityInfo(cityInfo);
        } else {
            exceptionMessage.setIp("空");
            exceptionMessage.setMethod("空");
            exceptionMessage.setQueryString("空");
            exceptionMessage.setRequestURI("空");
            exceptionMessage.setCityInfo("空");
        }
        return exceptionMessage;
    }
    private String getStackTraceAsString(Exception exception) {
        StringWriter stringWriter = new StringWriter();
        exception.printStackTrace(new PrintWriter(stringWriter));
        return stringWriter.toString();
    }
}
